/*
 * 气体检测传感器 适用于慧闻科技相关传感器
 * 命令示例：
{
  "method": "get",
  "obj": ["co","ch4","tvoc","c2h5oh","ch20"]
}
*/
#include "Arduino.h"    
#include <OneButton.h>       
#include <WiFi.h>
#include <SDDC_SDK_lib.h>
#include <cJSON.h>
#include <Wire.h>

#define SDDC_CFG_PORT             680U             // SDDC 协议使用的端口号
#define PIN_INPUT                 0                // 选择 IO0 进行控制
#define ESP_TASK_STACK_SIZE       4096
#define ESP_TASK_PRIO             25

static const char* ssid     = "EOS-Tenda";         // WiFi 名
static const char* password = "1234567890";        // WiFi 密码
  
OneButton button(PIN_INPUT, true);


static double data_ch4; // CH4 浓度
static double  warn_report_data = 3000.0;    // CH4警告上报值

#define UART_AIR_NAME_CH4 "ch4"

#define AIR_INFO_TYPE_2

#define FRAME_HEADER_AA 0xaa
#define FRAME_HEADER_2C 0x2c
#define FRAME_HEADER_E4 0xe4

#define WARN_CH4 0x09

#ifdef AIR_INFO_TYPE_1
#define BUF_SIZE 40
#endif
#ifdef AIR_INFO_TYPE_2
#define BUF_SIZE 24
#endif
#ifdef  AIR_INFO_TYPE_3
#define BUF_SIZE 9
#endif

static void clear_data (double data)
{
    data = 0.0;
}

/* 
 *  设备上报接口
 */
static void uart_report_state(char * name)
{
    cJSON * root = cJSON_CreateObject();
    cJSON * array = cJSON_CreateArray();
    cJSON_AddItemToArray(array, cJSON_CreateString(name));
    cJSON_AddItemToObject(root, "obj", array);
    object_report(root);
}

/* 
 *  计算 CheckSum 接口
 */
static uint8_t CheckSum(uint8_t *p, uint8_t len)
{
    uint8_t i;
    uint8_t sum = 0;
    for(i = 0;i < len;i++)
    {
        sum += p[i];
    }
    sum = sum & (0xFF);
    return sum;
}

/* 
 *  内部设置数据接口
 */
static void uart_value_set(uint8_t *buf)
{
    if (BUF_SIZE == 24) {
        if(buf[17] == WARN_CH4) {
            data_ch4 = ((((buf[21]*256)+buf[20])*256+buf[19])*256+buf[18])/1000;
        } else {
            clear_data(data_ch4);
        }
    }
}

/* 
 *  检查帧头
 */
static sddc_bool_t check_frame_header (uint8_t *buf)
{
    int i = 0;
    while (Serial2.available()) {   
        buf[i] = (uint8_t)Serial2.read(); 
        if(i == 0 && buf[0] != FRAME_HEADER_AA) {
//        if(i == 0 && buf[0] == FRAME_HEADER_2C && buf[1] == FRAME_HEADER_E4){
            return SDDC_FALSE;
        }               
        i++;       
        if(i == BUF_SIZE)
        {    
            i = 0;
            return SDDC_TRUE;
        } 
    }
    return SDDC_TRUE;
}

/* 
 *  气体传感器循环读取task
 */
static void Air_sensor_task(void *arg)
{
    uint8_t buf[BUF_SIZE];
    uint8_t * p_buf = &buf[0];
    while(1) {
        usleep(1000 * 3000);
        if(check_frame_header(p_buf)) {
            if (CheckSum(p_buf, (BUF_SIZE-1)) != buf[BUF_SIZE-1]) {
                sddc_printf("data checksum fail ...\n");
            } else {
                sddc_printf("data checksum success ...\n");
                uart_value_set(p_buf);
                if(buf[BUF_SIZE-2]) {
                    uart_value_set(p_buf);
                }
            }
            if (data_ch4 >= warn_report_data) {
                uart_report_state(UART_AIR_NAME_CH4);
            }
                
        }
    }
}

/* 
 *  uart 设备初始化
 */
static void uart_dev_init(void)
{
    // 打印串口初始化
    Serial.begin(115200);
    Serial.setDebugOutput(true);
    Serial.println();
    
    // 传感器使用串口初始化
    Serial2.begin(9600);
    Serial2.setDebugOutput(true);
    Serial2.println();
    xTaskCreate(Air_sensor_task, "Air_sensor_task", ESP_TASK_STACK_SIZE, NULL, ESP_TASK_PRIO, NULL);

}

sddc_bool_t warning_val_set(const uint64_t value)
{
    warn_report_data = value;
    return SDDC_TRUE;
}

/* 
 *  uart_dev 状态获取函数
 */
sddc_bool_t uart_dev_state_get(char *objvalue, int value_len)
{
    sprintf(objvalue, "%lf", data_ch4);
    value_len = sizeof(data_ch4);
    return SDDC_TRUE;
}
 
/* 
 *  数字量设备对象函数与处理方法注册
 */
NUM_DEV_REGINFO num_dev[] = {
        {UART_AIR_NAME_CH4,warning_val_set},
};

/*
 *  显示设备对象函数与处理方法注册
 */
DIS_DEV_REGINFO dis_dev[] = {
};

/*
 * IO设备对象设置函数与处理方法注册
 */
IO_DEV_REGINFO io_dev[] = {
};

/*
 *  系统对象状态获取注册
 */
DEV_STATE_GET  dev_state_get_reg[] = {
        {UART_AIR_NAME_CH4,   DEV_NUM_TYPE,  uart_dev_state_get},
};

/*
 *  当前设备的信息定义
 */
DEV_INFO    dev_info = {
            .name     = "CH4检测",
            .type     = "gas.ch4",
            .excl     = SDDC_FALSE,
            .desc     = "MHD2002 + ESP32S",
            .model    = "IDCH401B",
            .vendor   = "inspiration-desktop",
};

/*
 *   系统注册对象汇聚
 */
SDDC_CONFIG_INFO sys_cfg = {
        .token             = "1234567890",            // 设备密码
        .devinfo           = &dev_info,                
        .io_dev_reg        = io_dev,
        .io_dev_reg_num    = ARRAY_SIZE(io_dev),
        .num_dev_reg       = num_dev,
        .num_dev_reg_num   = ARRAY_SIZE(num_dev),
        .state_get_reg     = dev_state_get_reg,
        .state_get_reg_num = ARRAY_SIZE(dev_state_get_reg),
        .dis_dev_reg       = dis_dev,
        .dis_dev_num       = ARRAY_SIZE(dis_dev),
};


/*
 * IO0 按键检测任务
 */
static void esp_io0_key_task()
{
    WiFi.disconnect();
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(1000);
        Serial.println("WIFI Wait for Smartconfig");
        
        // 设置为Station模式
        WiFi.mode(WIFI_STA);
        
        // 开始智能配网
        WiFi.beginSmartConfig();
        while (1)
        {
            delay(500);
            // 检查智能配网
            if (WiFi.smartConfigDone())
            {
                // 设置自动连接
                WiFi.setAutoConnect(true);  
                break;
            }
        }
    }
}
/*
 * 循环扫描按键
 */
static void esp_tick_task(void *arg)
{
    void *sddc = arg;
    (void)sddc;
    while(1)
    {
        button.tick();
        delay(100);
    }
}

void setup() {
    byte mac[6];
    
    uart_dev_init();
    
    // 启动 WiFi 并且连接网络
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) 
    {
        delay(500);
        Serial.print(".");
    }
  
    // 获取并打印 IP 地址
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.print("'ip :");
    Serial.print(WiFi.localIP());
    Serial.println("' to connect"); 
  
    // 清除一下按键状态机的状态
    button.reset();
  
    // 创建按键扫描线程，长按 IO0 按键，松开后ESP32 将会进入 SmartConfig 模式
    sddc_printf("长按按键进入 Smartconfig...\n");
    button.attachLongPressStop(esp_io0_key_task);
    xTaskCreate(esp_tick_task, "button_tick", ESP_TASK_STACK_SIZE, NULL, ESP_TASK_PRIO, NULL);

    // sddc协议初始化
    sddc_lib_main(&sys_cfg);

    // 获取并打印网卡 mac 地址
    WiFi.macAddress(mac);
    sddc_printf("MAC addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
              mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]);
    // 使用网卡 mac 地址设置设备唯一标识 UID
    sddc_set_uid(G_sddc, mac);
}

void loop() {
    // 运行 SDDC 协议循环
    while (1) 
    {
        sddc_printf("SDDC running...\n");
        sddc_run(G_sddc);
        sddc_printf("SDDC quit!\n");
    }

    // 销毁 SDDC 协议
    sddc_destroy(G_sddc);
}
